package com.hnx.admin.common.security.config;

import cn.hutool.core.util.ArrayUtil;
import com.hnx.admin.common.security.core.SecurityProperties;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Arrays;

/*
创建人： 倔强的头发
创建时间：2024/7/13
功能描述:

*/
@Configuration
@RequiredArgsConstructor
@EnableConfigurationProperties(SecurityProperties.class)
public class SecurityConfiguration {
    private final SecurityProperties securityProperties;
    /**
     * 自定义Web安全配置。
     * <p>
     * 该方法用于根据配置确定是否忽略特定的API请求。如果配置了开放的API列表，
     * 则这些API请求将不会进行Spring Security的安全检查。
     *
     * @return WebSecurityCustomizer 实例，用于配置Web安全。
     */
    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        String[] openApi = securityProperties.getOpenApi();
        if (null == openApi || ArrayUtil.isEmpty(openApi)) {
            return web -> {};
        }
        return web -> web.ignoring().requestMatchers(openApi);
    }
    /**
     * 根据安全配置创建并返回BCryptPasswordEncoder实例。
     * 注意：应确保securityProperties中的密码长度设置适当且安全。
     *
     * @return BCryptPasswordEncoder 实例
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        Integer passwordEncoderLength = securityProperties.getPasswordEncoderLength();
        // 确保传入的强度在BCryptPasswordEncoder支持的范围内
        // 假设标准BCrypt使用2^13（8192）位的盐值作为参考
        if (passwordEncoderLength < 4 || passwordEncoderLength > 31) {
            throw new IllegalArgumentException("密码强度（盐值位数）应在4到31之间");
        }
        return new BCryptPasswordEncoder(passwordEncoderLength);
    }
    /**
     * 配置CORS（跨源资源共享）源。
     */
    @Bean
    public CorsConfigurationSource corsConfigSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configureAllowedHeaders(configuration);
        configureAllowedOrigins(configuration);
        configureAllowedMethods(configuration);
        configuration.setMaxAge(3600L);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
    /**
     * 配置CORS配置对象的允许头部。
     * 此方法用于设置哪些HTTP头部是跨域请求所允许的
     * 通过这种方式，可以严格控制跨域请求中哪些头部信息可以被浏览器访问。
     *
     * @param configuration CORS配置对象，用于设置跨域请求的配置。
     */
    private void configureAllowedHeaders(CorsConfiguration configuration) {
        configuration.setAllowedHeaders(Arrays.asList(securityProperties.getAllowedHeaders()));
    }

    private void configureAllowedOrigins(CorsConfiguration configuration) {
        // 提高安全性：明确指定允许的来源，默认为"*"，但在实际应用中，应该替换为具体的域名或进行动态配置
        configuration.setAllowedOrigins(Arrays.asList(securityProperties.getAllowedOrigins()));
    }
    /**
     * 配置CORS配置对象的允许方法。
     * 这样做的目的是为了限制哪些HTTP方法可以从不同的源访问此API。
     *
     * @param configuration CORS配置对象，用于设置允许的方法。
     */
    private void configureAllowedMethods(CorsConfiguration configuration) {
        configuration.setAllowedMethods(Arrays.asList(securityProperties.getAllowedMethods()));
    }
}
